package com.sdk.app.util;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
import android.util.Log;

public class AppInfoUtil {
	private static final String TAG = "AppInfoUtil";

	/**
	 * @param context
	 * @return List<CacheAppInfo> 获得用户安装的apk信息
	 */
	public static List<CacheAppInfo> getUserAppInfos(Context context) {
		List<CacheAppInfo> appInfos = new ArrayList<CacheAppInfo>();
		CacheAppInfo appInfo = null;
		PackageManager pm = context.getPackageManager();
		List<PackageInfo> packageInfos = pm.getInstalledPackages(0);

		for (PackageInfo packageInfo : packageInfos) {
			ApplicationInfo applicationInfo = packageInfo.applicationInfo;
			if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
				appInfo = new CacheAppInfo();
				String packageName = packageInfo.packageName;
				appInfo.setAppIcon(applicationInfo.loadIcon(pm));
				appInfo.setAppLabel(applicationInfo.loadLabel(pm).toString());
				appInfo.setAppVersionCode(packageInfo.versionCode);
				appInfo.setAppPackageName(packageName);
				appInfo.setVersionName(packageInfo.versionName);
				appInfo.setAppSize(formatSize(getSourceFile(applicationInfo)));
				appInfo.setAppSourceDir(applicationInfo.sourceDir);
				appInfos.add(appInfo);
			}
		}
		return appInfos;
	}

	/**
	 * @param context
	 * @return List<CacheAppInfo> 获得所有安装包的apk信息
	 */
	public static List<CacheAppInfo> getAllAppInfos(Context context) {
		List<CacheAppInfo> appInfos = new ArrayList<CacheAppInfo>();
		CacheAppInfo appInfo = null;
		PackageManager pm = context.getPackageManager();
		List<PackageInfo> packageInfos = pm.getInstalledPackages(0);

		for (PackageInfo packageInfo : packageInfos) {
			ApplicationInfo applicationInfo = packageInfo.applicationInfo;
			appInfo = new CacheAppInfo();
			String packageName = packageInfo.packageName;
			appInfo.setAppIcon(applicationInfo.loadIcon(pm));
			appInfo.setAppLabel(applicationInfo.loadLabel(pm).toString());
			appInfo.setAppVersionCode(packageInfo.versionCode);
			appInfo.setAppPackageName(packageName);
			appInfo.setVersionName(packageInfo.versionName);
			appInfo.setAppSize(formatSize(getSourceFile(applicationInfo)));
			appInfo.setAppSourceDir(applicationInfo.sourceDir);
			appInfos.add(appInfo);
		}
		return appInfos;
	}

	/**
	 * @param context
	 * @return 获得系统apk 的 List<CacheAppInfo>
	 */
	public static List<CacheAppInfo> getSystemAppInfos(Context context) {
		List<CacheAppInfo> appInfos = new ArrayList<CacheAppInfo>();
		CacheAppInfo appInfo = null;
		PackageManager pm = context.getPackageManager();
		List<PackageInfo> packageInfos = pm.getInstalledPackages(0);
		for (PackageInfo packageInfo : packageInfos) {
			ApplicationInfo applicationInfo = packageInfo.applicationInfo;
			if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
				appInfo = new CacheAppInfo();
				String packageName = packageInfo.packageName;
				appInfo.setAppIcon(applicationInfo.loadIcon(pm));
				appInfo.setAppLabel(applicationInfo.loadLabel(pm).toString());
				appInfo.setAppVersionCode(packageInfo.versionCode);
				appInfo.setAppPackageName(packageName);
				appInfo.setVersionName(packageInfo.versionName);
				appInfo.setAppSize(formatSize(getSourceFile(applicationInfo)));
				appInfo.setAppSourceDir(applicationInfo.sourceDir);
				appInfos.add(appInfo);
			}
		}
		return appInfos;
	}

	/**
	 * @param applicationInfo
	 * @return 获得 applicationInfo.sourceDir的 File
	 */
	public static File getSourceFile(ApplicationInfo applicationInfo) {
		final String path = applicationInfo.sourceDir;
		final File file = new File(path);
		if (file.exists()) {
			return file;
		} else {
			return null;
		}
	}

	/**
	 * @param file
	 * @return 计算出文件的大小,以String格式返回,例如：2.13M
	 */
	public static String formatSize(File file) {
		if (null != file) {
			final long size = file.length();
			final String sizeString = String.valueOf(size >> 10);

			if (Integer.parseInt(sizeString) >= 1024) {
				double msize = Integer.parseInt(sizeString) / (double) 1024;
				Log.i("mize", String.valueOf(msize));
				return format(msize) + "M";
			}

			return sizeString + "KB";
		} else {
			return "UNKNOWN";
		}
	}

	/**
	 * @param count
	 * @return 对double保留两位小数点
	 */
	private static String format(double count) {
		NumberFormat nf = new DecimalFormat("0.00");
		return nf.format(count);
	}

	/**
	 * @param context
	 * @return 返回能够移动到Sd卡上的app信息
	 */
	public static List<CacheAppInfo> getCanMoveToSDCardApp(Context context) {
		List<CacheAppInfo> appInfos = new ArrayList<CacheAppInfo>();
		CacheAppInfo appInfo = null;
		PackageManager pm = context.getPackageManager();
		List<PackageInfo> packageInfos = pm.getInstalledPackages(0);
		for (PackageInfo packageInfo : packageInfos) {
			ApplicationInfo applicationInfo = packageInfo.applicationInfo;
			if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
					&& (applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0
					&& (checkAppPosition(context, packageInfo.packageName) != 1)) {
				appInfo = new CacheAppInfo();
				String packageName = packageInfo.packageName;
				appInfo.setAppIcon(applicationInfo.loadIcon(pm));
				appInfo.setAppLabel(applicationInfo.loadLabel(pm).toString());
				appInfo.setAppVersionCode(packageInfo.versionCode);
				appInfo.setAppPackageName(packageName);
				appInfo.setVersionName(packageInfo.versionName);
				appInfo.setAppSize(formatSize(getSourceFile(applicationInfo)));
				appInfo.setAppSourceDir(applicationInfo.sourceDir);
				appInfos.add(appInfo);
			}
		}
		return appInfos;
	}

	/**
	 * 
	 * @param context
	 * @param packageName
	 * @return 0-auto,1-internalOnly,2-preferExternal
	 */
	public static int checkAppPosition(Context context, String packageName) {
		final int auto = 0;
		final int internalOnly = 1;
		final int preferExternal = 2;
		XmlResourceParser xml = null;
		try {
			xml = context.createPackageContext(packageName, 0).getAssets()
					.openXmlResourceParser("AndroidManifest.xml");
			int eventType = xml.getEventType();
			xmlloop: while (eventType != XmlPullParser.END_DOCUMENT) {
				switch (eventType) {
				case XmlPullParser.START_TAG:
					if (!xml.getName().matches("manifest")) {
						break xmlloop;
					} else {
						for (int j = 0; j < xml.getAttributeCount(); j++) {
							if (xml.getAttributeName(j).matches(
									"installLocation")) {
								// to do
								Log.i("xml", xml.getAttributeValue(j));
								switch (checkInstallLocation(xml
										.getAttributeValue(j))) {
								default:
								case auto:// auto
									return 0;
								case internalOnly:// internalOnly
									return 1;
								case preferExternal:// preferExternal
									return 2;
								}
							}
						}
						return 1;
					}
				}
				eventType = xml.nextToken();
			}

		} catch (NameNotFoundException e) {
			Log.e(TAG, e.getMessage());
		} catch (IOException e) {
			Log.e(TAG, e.getMessage());
		} catch (XmlPullParserException e) {
			Log.e(TAG, e.getMessage());
		} finally {
			if (null != xml) {
				xml.close();
				xml = null;
			}
		}
		return 1;
	}

	/**
	 * @param string
	 * @return 返回安装位置的标示
	 */
	private static int checkInstallLocation(String string) {
		if (string.equals("auto")) {
			return 0;
		}
		if (string.equals("internalOnly") || string.equals("1")) {
			return 1;
		}
		if (string.equals("preferExternal")) {
			return 2;
		}
		return -1;
	}

	/**
	 * @return 返回所有apk的路径
	 */
	public List<String> findAllPackage(Context context) {

		String path = getExternalStoragePath();
		// String forbidScanFolder = path + File.pathSeparator +
		// Backup.DEST_FOLDER_APPMANAGER;
		Log.v(TAG, "findAllPackageInPhone path = " + path);
		String genPath = path;
		List<String> paths = new ArrayList<String>();
		List<String> apkPaths = new ArrayList<String>();
		if (null == path) {
			Log.v(TAG, "path is null, or can not read.");
			return apkPaths;
		}
		paths.add(genPath);

		while (true) {
			if (paths.size() == 0) {
				break;
			}

			String currentPath = paths.get(0);
			paths.remove(0);

			if (null != new File(currentPath)) {
				if (new File(currentPath).isDirectory()) {
					File[] files = new File(currentPath).listFiles();
					if (null != files) {
						for (int i = 0; i < files.length; i++) {
							File f = files[i];
							if (null != f) {
								String fPath = f.getAbsolutePath();
								if (f.isDirectory()) {
									paths.add(fPath);
								} else if (f.isFile()) {
									String packageName = f.getName();
									if (checkIsApkFile(packageName)) {
										apkPaths.add(fPath);
										CacheAppInfo appInfo = getAppInfoFromPath(
												context, fPath);
										if (null != mOnFindAPKListener) {
											mOnFindAPKListener
													.onFindAPK(appInfo);
										}
									}
								}
							}
						}
					}
				}
			}
		}
		Log.v(TAG, "size = " + apkPaths.size());
		return apkPaths;
	}

	/**
	 * @param packageName
	 * @return 检查其实不是apk
	 */
	private static boolean checkIsApkFile(String packageName) {
		if (null == packageName) {
			return false;
		}
		String[] strs = packageName.split("\\.");
		if (strs.length > 1) {
			String posifix = strs[strs.length - 1];
			if (posifix.toLowerCase().equals("apk")) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 获取SdCard路径
	 */
	public static String getExternalStoragePath() {

		// 获取SdCard状态
		String state = android.os.Environment.getExternalStorageState();

		// 判断SdCard是否存在并且是可用的

		if (android.os.Environment.MEDIA_MOUNTED.equals(state)) {
			if (android.os.Environment.getExternalStorageDirectory().canRead()) {
				return android.os.Environment.getExternalStorageDirectory()
						.getPath();
			}
		}
		Log.v(TAG, "Sdcard is not avaliable.");
		return null;
	}

	/**
	 * 
	 * @param ctx
	 * @param apkPath
	 * @return 根据apk的路径，拿到此apk的相关信息
	 */
	public static CacheAppInfo getAppInfoFromPath(Context ctx, String apkPath) {
		File apkFile = new File(apkPath);
		if (!apkFile.exists()) {
			return null;
		}
		String PATH_PackageParser = "android.content.pm.PackageParser";
		String PATH_AssetManager = "android.content.res.AssetManager";
		try {
			CacheAppInfo pi;
			// 反射得到pkgParserCls对象并实例化,有参数
			Class<?> pkgParserCls = Class.forName(PATH_PackageParser);
			Class<?>[] typeArgs = { String.class };
			Constructor<?> pkgParserCt = pkgParserCls.getConstructor(typeArgs);
			Object[] valueArgs = { apkPath };
			Object pkgParser = pkgParserCt.newInstance(valueArgs);

			// 从pkgParserCls类得到parsePackage方法
			DisplayMetrics metrics = new DisplayMetrics();
			metrics.setToDefaults();// 这个是与显示有关的, 这边使用默认
			typeArgs = new Class<?>[] { File.class, String.class,
					DisplayMetrics.class, int.class };
			Method pkgParser_parsePackageMtd = pkgParserCls.getDeclaredMethod(
					"parsePackage", typeArgs);

			valueArgs = new Object[] { new File(apkPath), apkPath, metrics, 0 };

			// 执行pkgParser_parsePackageMtd方法并返回
			Object pkgParserPkg = pkgParser_parsePackageMtd.invoke(pkgParser,
					valueArgs);

			// 从返回的对象得到名为"applicationInfo"的字段对象
			if (pkgParserPkg == null) {
				return null;
			}
			Field appInfoFld = pkgParserPkg.getClass().getDeclaredField(
					"applicationInfo");

			// 从对象"pkgParserPkg"得到字段"appInfoFld"的值
			if (appInfoFld.get(pkgParserPkg) == null) {
				return null;
			}
			ApplicationInfo info = (ApplicationInfo) appInfoFld
					.get(pkgParserPkg);

			// 反射得到assetMagCls对象并实例化,无参
			Class<?> assetMagCls = Class.forName(PATH_AssetManager);
			Object assetMag = assetMagCls.newInstance();
			// 从assetMagCls类得到addAssetPath方法
			typeArgs = new Class[1];
			typeArgs[0] = String.class;
			Method assetMag_addAssetPathMtd = assetMagCls.getDeclaredMethod(
					"addAssetPath", typeArgs);
			valueArgs = new Object[1];
			valueArgs[0] = apkPath;
			// 执行assetMag_addAssetPathMtd方法
			assetMag_addAssetPathMtd.invoke(assetMag, valueArgs);

			// 得到Resources对象并实例化,有参数
			Resources res = ctx.getResources();
			typeArgs = new Class[3];
			typeArgs[0] = assetMag.getClass();
			typeArgs[1] = res.getDisplayMetrics().getClass();
			typeArgs[2] = res.getConfiguration().getClass();
			Constructor<Resources> resCt = Resources.class
					.getConstructor(typeArgs);
			valueArgs = new Object[3];
			valueArgs[0] = assetMag;
			valueArgs[1] = res.getDisplayMetrics();
			valueArgs[2] = res.getConfiguration();
			res = resCt.newInstance(valueArgs);

			// 读取apk文件的信息
			pi = new CacheAppInfo();
			if (info != null) {
				if (info.icon != 0) {// 图片存在，则读取相关信息
					Drawable icon = res.getDrawable(info.icon);// 图标
					pi.setAppIcon(icon);
				}
				if (info.labelRes != 0) {
					String name = (String) res.getText(info.labelRes);// 名字
					pi.setAppLabel(name);
				} else {
					String apkName = apkFile.getName();
					pi.setAppLabel(apkName.substring(0,
							apkName.lastIndexOf(".")));
				}
				pi.setAppPackageName(info.packageName);
			} else {
				return null;
			}
			PackageManager pm = ctx.getPackageManager();
			PackageInfo packageInfo = pm.getPackageArchiveInfo(apkPath,
					PackageManager.GET_ACTIVITIES);
			if (packageInfo != null) {
				pi.setVersionName(packageInfo.versionName);
			}
			// 判断此应用是否已经安装

			pi.setAppSize(formatSize(apkFile));
			pi.setAppSourceDir(apkPath);
			return pi;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	private OnFindAPKListener mOnFindAPKListener;

	public void setOnFindAPKListener(OnFindAPKListener mOnFindAPKListener) {
		this.mOnFindAPKListener = mOnFindAPKListener;
	}

	public interface OnFindAPKListener {
		void onFindAPK(CacheAppInfo appInfo);
	}

}

class CacheAppInfo {
	Drawable appIcon;
	String versionName = "";
	int appVersionCode;
	String appSize = "";
	String appSourceDir = "";
	String appLabel = "";
	String appPackageName = "";

	public Drawable getAppIcon() {
		return appIcon;
	}

	public void setAppIcon(Drawable appIcon) {
		this.appIcon = appIcon;
	}

	public String getVersionName() {
		return versionName;
	}

	public void setVersionName(String versionName) {
		this.versionName = versionName;
	}

	public int getAppVersionCode() {
		return appVersionCode;
	}

	public void setAppVersionCode(int appVersionCode) {
		this.appVersionCode = appVersionCode;
	}

	public String getAppSize() {
		return appSize;
	}

	public void setAppSize(String appSize) {
		this.appSize = appSize;
	}

	public String getAppSourceDir() {
		return appSourceDir;
	}

	public void setAppSourceDir(String appSourceDir) {
		this.appSourceDir = appSourceDir;
	}

	public String getAppLabel() {
		return appLabel;
	}

	public void setAppLabel(String appLabel) {
		this.appLabel = appLabel;
	}

	public String getAppPackageName() {
		return appPackageName;
	}

	public void setAppPackageName(String appPackageName) {
		this.appPackageName = appPackageName;
	}

}
